package com.android.settings.audio.peq.peq.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

public class GainView extends View {

    private static final String TAG = "GainView";

    private int[] mGains;

    private Paint mPain;

    public GainView(Context context) {

        this(context, null);

    }

    public GainView(Context context, AttributeSet attrs) {

        this(context, attrs, 0);

    }

    public GainView(Context context, AttributeSet attrs, int defStyle) {

        super(context, attrs, defStyle);

//8个点，对应8个频段

        mGains = new int[]{0, 0, 0, 0, 0, 0, 0, 0};

        mPain = new Paint();

    }

    //更新增益曲线
    public void updateGain(int index, int gain) {
        mGains[index] = gain;

        postInvalidate();

    }

    //横纵坐标
    private int[] getXIndex() {

        int[] xs = new int[8];

        int realWidth = getWidth() - getPaddingLeft() - getPaddingRight();

        int step = realWidth / (xs.length - 1);

        for (int i = 0; i < xs.length; i++) {

            xs[i] = step * i;

        }

        return xs;

    }

    @SuppressLint("DrawAllocation")

    @Override

    protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);

        int[] gains = new int[mGains.length];

        for (int i = 0; i < mGains.length; i++) {

            gains[i] = -mGains[i];

        }

        int[] xs = getXIndex();

        int[] ys = new int[xs.length];

        int half_height = (getHeight() - getPaddingTop() - getPaddingBottom()) / 2;

        for (int i = 0; i < ys.length; i++) {

            ys[i] = half_height * gains[i] / 12 + half_height;

        }

        double min = Double.MAX_VALUE;

        double max = Double.MIN_VALUE;

        for (int x : xs) {

            if (x > max)

                max = x;

            if (x < min)

                min = x;

        }

        int sc = canvas.save();

        int left = getPaddingLeft();

        int top = getPaddingTop();

        canvas.translate(left, top);

        Paint p = mPain;

        p.setAntiAlias(true);

        p.setStyle(Paint.Style.STROKE);

        p.setStrokeWidth(2);

        p.setColor(Color.parseColor("#959595"));

//画中间线

        canvas.drawLine(0, half_height, getWidth() - getPaddingRight() - getPaddingLeft(), half_height, p);

        PointF[] points = new PointF[xs.length];

        for (int i = 0; i < points.length; i++) {

            points[i] = new PointF();

            points[i].x = xs[i];

            points[i].y = ys[i];

        }

        List pointFList = getControlPoints(points);

        p.setStrokeWidth(3);

        p.setColor(Color.BLACK);

        Path path = new Path();

        createCurve2(pointFList, points, path);

//createCurve(points, path);

        canvas.drawPath(path, p);

//        p.setStyle(Paint.Style.FILL);

//        p.setStrokeWidth(1);

//        p.setColor(Color.LTGRAY);

//        p.setTextSize(8 * getContext().getResources().getDisplayMetrics().scaledDensity);

//        p.setFakeBoldText(true);

//        float density = getContext().getResources().getDisplayMetrics().density;

//        canvas.drawText("+12db", 5, 10 * density, p);

//        canvas.drawText("-12db", 5, getHeight() - getPaddingBottom() - 3 * density, p);

        canvas.restoreToCount(sc);

    }

    void createCurve(PointF[] originPoint, Path path) {

        int originCount = originPoint.length;

        float scale = 0.6f;

        PointF[] midPoints = new PointF[originCount];

// 生成中点

        for (int i = 0; i < originCount; i++) {

            int next = (i + 1) % originCount;

            midPoints[i] = new PointF();

            if (i == originCount - 1) {

//midPoints[i].x = getWidth() - getPaddingEnd();

                midPoints[i].x = originPoint[i].x;

                midPoints[i].y = (originPoint[i].y + (getHeight() - getPaddingTop() - getPaddingBottom()) / 2.0f) / 2.0f;

            } else {

                midPoints[i].x = (originPoint[i].x + originPoint[next].x) / 2.0f;

                midPoints[i].y = (originPoint[i].y + originPoint[next].y) / 2.0f;

            }

//            Log.d(TAG, " originPoint[" + i + "]= [" + originPoint[i].x + ", " + originPoint[i].y + "]");

//            Log.d(TAG, " midPoints[" + i + "]= [" + midPoints[i].x + ", " + midPoints[i].y + "]");

        }

// 平移中点

        PointF[] extraPoints = new PointF[2 * originCount];

        for (int i = 0; i < originCount; i++) {

            int back = (i + originCount - 1) % originCount;

            if (i == 0)

                back = 0;

            PointF midInMid = new PointF();

            midInMid.x = (midPoints[i].x + midPoints[back].x) / 2.0f;

            midInMid.y = (midPoints[i].y + midPoints[back].y) / 2.0f;

            float offsetX = originPoint[i].x - midInMid.x;

            float offsetY = originPoint[i].y - midInMid.y;

            int extraIndex = 2 * i;

            extraPoints[extraIndex] = new PointF();

            extraPoints[extraIndex].x = midPoints[back].x + offsetX;

            extraPoints[extraIndex].y = midPoints[back].y + offsetY;

// 朝 originPoint[i]方向收缩

            float addx = (extraPoints[extraIndex].x - originPoint[i].x) * scale;

            float addy = (extraPoints[extraIndex].y - originPoint[i].y) * scale;

            extraPoints[extraIndex].x = originPoint[i].x + addx;

            extraPoints[extraIndex].y = originPoint[i].y + addy;

//Log.d(TAG, " extraPoints[" + extraIndex + "]= [" + extraPoints[extraIndex].x + ", " + extraPoints[extraIndex].y + "]");

            int extraNext = (extraIndex + 1) % (2 * originCount);

            extraPoints[extraNext] = new PointF();

            extraPoints[extraNext].x = midPoints[i].x + offsetX;

            extraPoints[extraNext].y = midPoints[i].y + offsetY;

// 朝 originPoint[i]方向收缩

            addx = (extraPoints[extraNext].x - originPoint[i].x) * scale;

            addy = (extraPoints[extraNext].y - originPoint[i].y) * scale;

            extraPoints[extraNext].x = originPoint[i].x + addx;

            extraPoints[extraNext].y = originPoint[i].y + addy;

//Log.d(TAG, " extraPoints[" + extraNext + "]= [" + extraPoints[extraNext].x + ", " + extraPoints[extraNext].y + "]");

        }

//控制点，即所在点位置

        PointF[] controlPoint = new PointF[4];

// 生成4控制点，产生贝塞尔曲线

        boolean first = true;

        for (int i = 0; i < originCount; i++) {

            controlPoint[0] = originPoint[i];

            int extraIndex = 2 * i;

            controlPoint[1] = extraPoints[extraIndex + 1];

            if (controlPoint[1].x < originPoint[i].x)

                controlPoint[1] = originPoint[i];

//Log.d(TAG, " controlPoint[0]= " + controlPoint[0] + ", controlPoint[1]= " + controlPoint[1]);

            int extraNext = (extraIndex + 2) % (2 * originCount);

            controlPoint[2] = extraPoints[extraNext];

            if (controlPoint[2].x < originPoint[i].x)

                controlPoint[2] = originPoint[i];

            int next = (i + 1) % originCount;

            controlPoint[3] = originPoint[next];

            if (controlPoint[3].x < originPoint[i].x)

                controlPoint[3] = originPoint[i];

//Log.d(TAG, " controlPoint[2]= " + controlPoint[2] + ", controlPoint[3]= " + controlPoint[3]);

            float u = 1;

            while (u >= 0) {

                float px = bezier3funcX(u, controlPoint);

                float py = bezier3funcY(u, controlPoint);

// u的步长决定曲线的疏密

                u -= 0.005;

//Log.d(TAG, "u= " + u + ", px= " + px + ", py= " + px );

                if (first) {

//画笔移动

                    path.moveTo(px, py);

//画笔标记清除

                    first = false;

                } else

//画笔划线

                    path.lineTo(px, py);

            }

        }

    }

    /**
     * 计算控制点
     * <p>
     * 斜率 y = kx + b => k = (y - b)/x
     *
     * @param points 原始点
     * @return 控制点
     */

    private List getControlPoints(PointF[] points) {

        List pointFList = new ArrayList<>();

        float rate = 0.3f;

        //从第一个控制点开始计算

        float cx1 = points[0].x + (points[1].x - points[0].x) * rate;

        float cy1 = points[0].y;

        pointFList.add(new PointF(cx1, cy1));

        for (int i = 1; i < points.length - 1; i++) {

            //下一个点
            float k = (points[i + 1].y - points[i - 1].y) / (points[i + 1].x - points[i - 1].x);

            float b = points[i].y - k * points[i].x;

            //左边控制点
            float cxLeft = points[i].x - (points[i].x - points[i - 1].x) * rate;

            float cyLeft = k * cxLeft + b;

            pointFList.add(new PointF(cxLeft, cyLeft));

            //右边控制点
            float cxRight = points[i].x + (points[i + 1].x - points[i].x) * rate;

            float cyRight = k * cxRight + b;

            pointFList.add(new PointF(cxRight, cyRight));

        }

        //最后一个点
        float cxLast = points[points.length - 1].x - (points[points.length - 1].x - points[points.length - 2].x) * rate;

        float cyLast = points[points.length - 1].y;

        pointFList.add(new PointF(cxLast, cyLast));

        return pointFList;

    }

    void createCurve2(List<PointF> pointFList, PointF[] originPoint, Path path) {

        for (int i = 0; i < originPoint.length - 1; i++) {

            path.moveTo(originPoint[i].x, originPoint[i].y);

            path.cubicTo(

                    pointFList.get(i * 2).x, pointFList.get(i * 2).y,

                    pointFList.get(i * 2 + 1).x, pointFList.get(i * 2 + 1).y,

                    originPoint[i + 1].x, originPoint[i + 1].y

            );

        }

    }

    /**
     * 三次贝塞尔曲线
     * <p>
     * B(t) = P0 * (1-t)^3 + 3 * P1 * t(1-t)^2 + 3 * P2 * t^2 * (1-t) + P3 *t^3
     *
     * @param uu       属于[0, 1]
     * @param controlP 控制点
     * @return X 坐标值
     */

    float bezier3funcX(float uu, PointF[] controlP) {

        float part0 = controlP[0].x * uu * uu * uu;

        float part1 = 3 * controlP[1].x * uu * uu * (1 - uu);

        float part2 = 3 * controlP[2].x * uu * (1 - uu) * (1 - uu);

        float part3 = controlP[3].x * (1 - uu) * (1 - uu) * (1 - uu);

        return part0 + part1 + part2 + part3;

    }

    float bezier3funcY(float uu, PointF[] controlP) {

        float part0 = controlP[0].y * uu * uu * uu;

        float part1 = 3 * controlP[1].y * uu * uu * (1 - uu);

        float part2 = 3 * controlP[2].y * uu * (1 - uu) * (1 - uu);

        float part3 = controlP[3].y * (1 - uu) * (1 - uu) * (1 - uu);

        return part0 + part1 + part2 + part3;

    }

}